package main.impl;

import java.awt.Point;

import main.api.GPSState;
import main.exception.NotAppliableException;

public class CopyOfGPSStateImpl implements GPSState {

	private TipoFicha estado[][];
	private Point posBlanco1;
	private Point posBlanco2;
	//funciona si se tiene una instancia por estado...
	//private TipoFicha espejado[][];
	
	public CopyOfGPSStateImpl(GPSState state) {
		super();
		int i,j;
		TipoFicha[][] aux = new TipoFicha[((CopyOfGPSStateImpl)state).estado.length][((CopyOfGPSStateImpl)state).estado[0].length];
		
		for(i=0; i< aux.length; i++)
			for(j=0; j< aux[0].length; j++)
				aux[i][j] = ((CopyOfGPSStateImpl)state).estado[i][j];
		
		this.estado = aux;
		//this.estado = ((GPSStateImpl)state).estado;
		this.posBlanco1 = ((CopyOfGPSStateImpl)state).posBlanco1;
		this.posBlanco2 = ((CopyOfGPSStateImpl)state).posBlanco2;
//		this.espejado = this.espejar(estado);
	}

	public CopyOfGPSStateImpl(TipoFicha[][] estado,Point posB1,Point posB2) {
		super();
		this.estado = estado;
		this.posBlanco1 = posB1;
		this.posBlanco2 = posB2;
		//this.espejado = this.espejar(estado);
	}

	public CopyOfGPSStateImpl() {
		super();
		TipoFicha[][] estado = {{TipoFicha.rectv,TipoFicha.cuadradog,TipoFicha.nada,TipoFicha.rectv},
				{TipoFicha.nada,TipoFicha.nada,TipoFicha.nada,TipoFicha.nada},
				{TipoFicha.rectv,TipoFicha.recth,TipoFicha.nada,TipoFicha.rectv},
				{TipoFicha.nada,TipoFicha.cuadradop,TipoFicha.cuadradop,TipoFicha.nada},
				{TipoFicha.cuadradop,TipoFicha.blanco1,TipoFicha.blanco2,TipoFicha.cuadradop}};
		
		this.estado = estado;
		this.posBlanco1 = new Point(4, 1);
		this.posBlanco2 = new Point(4, 2);
		//this.espejado = this.espejar(estado);
	}

	private TipoFicha [][] espejar(TipoFicha [][] estado) {
		TipoFicha[][] espejado = new TipoFicha[estado.length][estado[0].length];
		int i,j, aux;
		
		aux = estado[0].length;
		for (i = 0; i < estado.length; i++) {
			for (j = 0; j < aux; j++) {
				switch (estado[i][j]) {
					case cuadradog:
					case recth:
						espejado[i][aux-1-j] = TipoFicha.nada;
						j++;
						espejado[i][aux-1-j] = estado[i][j-1];
						break;
					default:
						espejado[i][aux-1-j] = estado[i][j];
						break;
				}
			}
		}
		return espejado;
	}
	@Override
	public boolean compare(GPSState state) {

		int i,j;
		boolean flag=false;
		
		for (i = 0; i < ((CopyOfGPSStateImpl)state).estado.length && !flag; i++) {
			for (j = 0; j < ((CopyOfGPSStateImpl)state).estado[i].length && !flag; j++) {
				if(!TipoFicha.equals(((CopyOfGPSStateImpl)state).estado[i][j], this.estado[i][j]))
					flag=true;
			}
		}
		
		TipoFicha[][] espejado = espejar(this.estado);
		
		if(flag) {
			for (i = 0; i < ((CopyOfGPSStateImpl)state).estado.length; i++) {
				for (j = 0; j < ((CopyOfGPSStateImpl)state).estado[i].length; j++) {
					if(!TipoFicha.equals(((CopyOfGPSStateImpl)state).estado[i][j], espejado[i][j]))
						return false;
				}
			}
		}
		
		return !flag;
	}

	public boolean tieneLugar(TipoFicha tipoFicha, Movimientos tipoMov) {
		switch (tipoFicha) {
		case blanco1:
			return checkMov(posBlanco1,tipoMov);
		case blanco2:
			return checkMov(posBlanco2,tipoMov);
		case blancoh:
			return checkMovH(tipoMov);
		case blancov:
			return checkMovV(tipoMov);
			
		default:
			break;
		}
		return false;
	}

	private boolean checkMovV(Movimientos tipoMov) {
		Point up = new Point();
		Point down = new Point();
		
		int xPos1 = posBlanco1.x;
		int yPos1 = posBlanco1.y;
		int xPos2 = posBlanco2.x;
		int yPos2 = posBlanco2.y;
		
		if(estado[posBlanco1.x][posBlanco1.y]!=TipoFicha.blanco1 && estado[posBlanco2.x][posBlanco2.y]!=TipoFicha.blanco2){
			System.out.println("pincho en check mov V");
			System.exit(0);
		}
		/*Chequeo si tiene un blanco arriba*/
		if( ((xPos1 - 1) > -1) && (this.estado[xPos1-1][yPos1] == TipoFicha.blanco2) ){
			up.setLocation(posBlanco1);
			down.setLocation(posBlanco2);
		}else{
			/*Chequeo si tiene un blanco abajo*/
			if( ((xPos1 + 1) < 5) && (this.estado[xPos1+1][yPos1] == TipoFicha.blanco2) ){
				up.setLocation(posBlanco2);
				down.setLocation(posBlanco1);
			}else
				return false;
		}
		/*Si llego ac�, es porque tengo dos blancos juntos*/
		
		switch (tipoMov) {
		case izquierda:
					if(yPos1 == 0)
						return false;
					/*Pregunto por la columna de la izquierda*/
					if(yPos1 == 1){
						return this.estado[up.x][up.y-1] == TipoFicha.rectv;
					}
					/*Pregunto adem�s por dos filas a la izquierda*/
						return (this.estado[up.x][up.y-2] == TipoFicha.cuadradog) || (this.estado[up.x][up.y-1] == TipoFicha.rectv);
		case derecha:
					if(yPos1 == 3)
						return false;
					/*Pregunto por columna de la derecha*/
					if(yPos1 == 2){
						return this.estado[up.x][up.y+1] == TipoFicha.rectv;
					}
					/*Pregunto adem�s por dos filas abajo*/
						return (this.estado[up.x][up.y+2] == TipoFicha.cuadradog) || (this.estado[up.x][up.y+1] == TipoFicha.rectv);
		default:
			break;
		}
		
		return false;
	}

	private boolean checkMovH(Movimientos tipoMov) {
		Point izq = new Point();
		Point der = new Point();
		
		int xPos1 = posBlanco1.x;
		int yPos1 = posBlanco1.y;
		int xPos2 = posBlanco2.x;
		int yPos2 = posBlanco2.y;
		//System.out.println(xPos1);
		//System.out.println(xPos2);
		//System.out.println(this);
		if(estado[posBlanco1.x][posBlanco1.y]!=TipoFicha.blanco1 && estado[posBlanco2.x][posBlanco2.y]!=TipoFicha.blanco2){
			System.out.println("pincho en check mov H");
			System.exit(0);
		}
		
		/*Chequeo si tiene un blanco a la derecha*/
		if( ((yPos1 + 1) < 4) && (this.estado[xPos1][yPos1+1] == TipoFicha.blanco2) ){
			//System.out.println("aca1");
			izq.setLocation(posBlanco1);
			der.setLocation(posBlanco2);
		}else{
			/*Chequeo si tiene un blanco a la izquierda*/
			if( ((yPos1 - 1) > -1) && (this.estado[xPos1][yPos1-1] == TipoFicha.blanco2) ){
				//System.out.println("aca2");
				izq.setLocation(posBlanco2);
				der.setLocation(posBlanco1);
			}else
				return false;
		}
		/*Si llego ac�, es porque tengo dos blancos juntos*/
		
		switch (tipoMov) {
		case arriba:
					if(xPos1 == 0)
						return false;
					/*Pregunto por la fila de arriba*/
					if(xPos1 == 1){
						return this.estado[izq.x-1][izq.y] == TipoFicha.recth;
					}
					/*Pregunto adem�s por dos filas arriba*/
					//System.out.println(xPos1);
					//System.out.println(izq.x);
					return (this.estado[izq.x-1][izq.y] == TipoFicha.recth) || (this.estado[izq.x-2][izq.y] == TipoFicha.cuadradog);
		case abajo:
					if(xPos1 == 4)
						return false;
					/*Pregunto por la fila de abajo*/
					if(xPos1 == 3){
						return this.estado[izq.x+1][izq.y] == TipoFicha.recth;
					}
					/*Pregunto adem�s por dos filas abajo*/
						return (this.estado[izq.x+1][izq.y] == TipoFicha.recth) || (this.estado[izq.x+2][izq.y] == TipoFicha.cuadradog);
		default:
			break;
		}
		
		return false;
	}

	private boolean checkMov(Point pos, Movimientos tipoMov) {
		int xPos = pos.x; /*FILA*/
		int yPos = pos.y; /*COLUMNA*/
		switch (tipoMov) {
		case arriba:
			
			if( ((xPos - 1) < 0) )
				return false;/*El blanco est� arriba de todo (primer fila)*/
			
			if (this.estado[xPos-1][yPos] == TipoFicha.cuadradop){
//				System.out.println("puedo mover hacia arriba");
				return true;
			}
			
			if( ((xPos - 2) < 0) )
				return false;/*El blanco est� en la segunda fila, y arriba no tiene un cuadradop*/
			
			if (this.estado[xPos-2][yPos] == TipoFicha.rectv) {
				return true;/*Dos posiciones m�s arriba tiene un rectv*/
			}
			break;
			
		case abajo:
			
			if( ((xPos + 1) > 4) )
				return false;/*El blanco est� abajo de todo (fila 5)*/
			
			if (this.estado[xPos+1][yPos] == TipoFicha.cuadradop)
				return true;
			
			if (this.estado[xPos+1][yPos] == TipoFicha.rectv) {
				return true;/*Dos posiciones m�s arriba tiene un rectv*/
			}
			break;
			
		case izquierda:
			
			if( ((yPos - 1) < 0) )
				return false;/*El blanco est� a la izquierda de  todo (primer columna)*/
			
			if (this.estado[xPos][yPos-1] == TipoFicha.cuadradop)
				return true;
			
			if( ((yPos - 2) < 0) )
				return false;/*El blanco est� en la segunda columna, y a la izquierda no tiene un cuadradop*/
			
			if (this.estado[xPos][yPos-2] == TipoFicha.recth)
				return true;
			break;
			
		case derecha:
			
			if( ((yPos + 1) > 3) )
				return false;/*El blanco est� a la derecha de todo (columna 4)*/
			
			if (this.estado[xPos][yPos+1] == TipoFicha.cuadradop)
				return true;
			
			if (this.estado[xPos][yPos+1] == TipoFicha.recth)
				return true;
			break;
			
		default:
			break;
		}
		
		return false;
		
	}

	public void move(TipoFicha tipoFicha, Movimientos tipoMov) throws NotAppliableException {
		if (!tieneLugar(tipoFicha, tipoMov)) {
			throw new NotAppliableException();
		}
		
		switch (tipoFicha) {
		case blanco1:
			//System.out.print(this.estado[posBlanco1.x][posBlanco1.y]);
//			System.out.println(this);
//			System.out.print("Cambio1 "+this.estado[posBlanco1.x][posBlanco1.y]);
			posBlanco1 = movePos(posBlanco1,tipoMov);
//			System.out.println("Por "+this.estado[posBlanco1.x][posBlanco1.y]);
//			System.out.println(this);
			break;
		case blanco2:
			//System.out.print(this.estado[posBlanco1.x][posBlanco1.y]);
//			System.out.print("Cambio2 "+this.estado[posBlanco2.x][posBlanco2.y]);
			posBlanco2 = movePos(posBlanco2,tipoMov);
//			System.out.println("Por "+this.estado[posBlanco2.x][posBlanco2.y]);
			break;
		case blancoh:
			//System.out.print(this.estado[posBlanco1.x][posBlanco1.y]);
			movePosH(tipoMov);break;
		case blancov:
			//System.out.print(this.estado[posBlanco1.x][posBlanco1.y]);
			movePosV(tipoMov);break;
			
		default:
			break;
		}
		
		//System.out.println(this);
	}

	private void movePosV(Movimientos tipoMov) {
		TipoFicha aux;
		//System.out.println("Blancos Antes:");
		//System.out.println(this.estado[posBlanco1.x][posBlanco1.y]);
		//System.out.println(this.estado[posBlanco2.x][posBlanco2.y]);
		if(estado[posBlanco1.x][posBlanco1.y]!=TipoFicha.blanco1 && estado[posBlanco2.x][posBlanco2.y]!=TipoFicha.blanco2){
			System.out.println("pincho entrando en mov V");
			System.exit(0);
		}
		
		Point up = new Point();
		Point down = new Point();
		
		int xPos1 = posBlanco1.x;
		int yPos1 = posBlanco1.y;
		int xPos2 = posBlanco2.x;
		int yPos2 = posBlanco2.y;
		
		/*Chequeo si tiene un blanco arriba*/
		if( ((xPos1 - 1) > -1) && (this.estado[xPos1-1][yPos1] == TipoFicha.blanco2) ){
			up.setLocation(posBlanco1);
			down.setLocation(posBlanco2);
		}else{
			/*Tiene un blanco abajo*/
				up.setLocation(posBlanco2);
				down.setLocation(posBlanco1);
		}
		
		switch (tipoMov) {
		case izquierda:
			/*Pregunto si en mi columna izquierda tengo un rectv*/
			if (this.estado[up.x][up.y-1] == TipoFicha.rectv){
				posBlanco1.y = posBlanco1.y-1;
				posBlanco2.y = posBlanco2.y-1;
				aux = this.estado[up.x][up.y];
				this.estado[up.x][up.y] = this.estado[up.x][up.y-1];
				this.estado[up.x][up.y-1] = aux;
				aux = this.estado[down.x][down.y];
				this.estado[down.x][down.y] = this.estado[down.x][down.y-1];
				this.estado[down.x][down.y-1] = aux;
			}else{ 
				/*Si no se dio lo anterior tengo que tener un cuadradog 2 columnas a la izquierda*/
				posBlanco1.y = posBlanco1.y-2;
				posBlanco2.y = posBlanco2.y-2;
				aux = this.estado[up.x][up.y];
				this.estado[up.x][up.y] = this.estado[up.x][up.y-1];
				this.estado[up.x][up.y-1] = this.estado[up.x][up.y-2];
				this.estado[up.x][up.y-2] = aux;
				aux = this.estado[down.x][down.y];
				this.estado[down.x][down.y] = this.estado[down.x][down.y-1];
				this.estado[down.x][down.y-1] = this.estado[down.x][down.y-2];
				this.estado[down.x][down.y-2] = aux;
			}
			break;
			
		case derecha:
			/*Pregunto si en mi columna derecha tengo un recth*/
			if (this.estado[up.x][up.y+1] == TipoFicha.rectv){
				posBlanco1.y = posBlanco1.y+1;
				posBlanco2.y = posBlanco2.y+1;
				aux = this.estado[up.x][up.y];
				this.estado[up.x][up.y] = this.estado[up.x][up.y+1];
				this.estado[up.x][up.y+1] = aux;
				aux = this.estado[down.x][down.y];
				this.estado[down.x][down.y] = this.estado[down.x][down.y+1];
				this.estado[down.x][down.y+1] = aux;
			}else{ 
				/*Si no se dio lo anterior tengo que tener un cuadradog 2 columnas a la derecha*/
				posBlanco1.y = posBlanco1.y+2;
				posBlanco2.y = posBlanco2.y+2;
				aux = this.estado[up.x][up.y];
				this.estado[up.x][up.y] = this.estado[up.x][up.y+1];
				this.estado[up.x][up.y+1] = this.estado[up.x][up.y+2];
				this.estado[up.x][up.y+2] = aux;
				aux = this.estado[down.x][down.y];
				this.estado[down.x][down.y] = this.estado[down.x][down.y+1];
				this.estado[down.x][down.y+1] = this.estado[down.x][down.y+2];
				this.estado[down.x][down.y+2] = aux;
			}
			break; 
		
	default:
		break;
	}
		if(estado[posBlanco1.x][posBlanco1.y]!=TipoFicha.blanco1 && estado[posBlanco2.x][posBlanco2.y]!=TipoFicha.blanco2){
			System.out.println("pincho saliendo de mov V");
			System.exit(0);
		}
		//System.out.println("Blancos Despues:");
		//System.out.println(this.estado[posBlanco1.x][posBlanco1.y]);
		//System.out.println(this.estado[posBlanco2.x][posBlanco2.y]);
	}

	private void movePosH(Movimientos tipoMov) {
		TipoFicha aux;
		
		//System.out.println("Blancos Antes:");
		//System.out.println(this.estado[posBlanco1.x][posBlanco1.y]);
		//System.out.println(this.estado[posBlanco2.x][posBlanco2.y]);
		if(estado[posBlanco1.x][posBlanco1.y]!=TipoFicha.blanco1 && estado[posBlanco2.x][posBlanco2.y]!=TipoFicha.blanco2){
			System.out.println("pincho entrando en mov H");
			System.exit(0);
		}
		
		Point izq = new Point();
		Point der = new Point();
		
		int xPos1 = posBlanco1.x;
		int yPos1 = posBlanco1.y;
		int xPos2 = posBlanco2.x;
		int yPos2 = posBlanco2.y;
		
		int flag = 0;
		
		/*Chequeo si tiene un blanco a la derecha*/
		if( ((yPos1 + 1) < 4) && (this.estado[xPos1][yPos1+1] == TipoFicha.blanco2) ){
			izq.setLocation(posBlanco1);
			der.setLocation(posBlanco2);
		}else{
			/*Tiene un blanco a la izquierda*/
				izq.setLocation(posBlanco2);
				der.setLocation(posBlanco1);
				flag = 1;
		}
		
		
		switch (tipoMov) {
			case arriba:
				/*Pregunto si en mi fila de arriba tengo un recth*/
				if (this.estado[izq.x-1][izq.y] == TipoFicha.recth){
					posBlanco1.x = posBlanco1.x-1;
					posBlanco2.x = posBlanco2.x-1;
					aux = this.estado[izq.x][izq.y];
					this.estado[izq.x][izq.y] = this.estado[izq.x-1][izq.y];
					this.estado[izq.x-1][izq.y] = aux;
					aux = this.estado[der.x][der.y];
					this.estado[der.x][der.y] = this.estado[der.x-1][der.y];
					this.estado[der.x-1][der.y] = aux;
				}else{ 
					/*Si no se dio lo anterior tengo que tener un cuadradog 2 filas antes*/
					posBlanco1.x = posBlanco1.x-2;
					posBlanco2.x = posBlanco2.x-2;
					aux = this.estado[izq.x][izq.y];
					this.estado[izq.x][izq.y] = this.estado[izq.x-1][izq.y];
					this.estado[izq.x-1][izq.y] = this.estado[izq.x-2][izq.y];
					this.estado[izq.x-2][izq.y] = aux;
					aux = this.estado[der.x][der.y];
					this.estado[der.x][der.y] = this.estado[der.x-1][der.y];
					this.estado[der.x-1][der.y] = this.estado[der.x-2][der.y];
					this.estado[der.x-2][der.y] = aux;
				}
				break;
				
			case abajo:
				/*Pregunto si en mi fila de abajo tengo un recth*/
				if (this.estado[izq.x+1][izq.y] == TipoFicha.recth){
					posBlanco1.x = posBlanco1.x+1;
					posBlanco2.x = posBlanco2.x+1;
					aux = this.estado[izq.x][izq.y];
					this.estado[izq.x][izq.y] = this.estado[izq.x+1][izq.y];
					this.estado[izq.x+1][izq.y] = aux;
					aux = this.estado[der.x][der.y];
					this.estado[der.x][der.y] = this.estado[der.x+1][der.y];
					this.estado[der.x+1][der.y] = aux;
				}else{ 
					/*Si no se dio lo anterior tengo que tener un cuadradog 2 filas abajo*/
					posBlanco1.x = posBlanco1.x+2;
					posBlanco2.x = posBlanco2.x+2;
					aux = this.estado[izq.x][izq.y];
					this.estado[izq.x][izq.y] = this.estado[izq.x+1][izq.y];
					this.estado[izq.x+1][izq.y] = this.estado[izq.x+2][izq.y];
					this.estado[izq.x+2][izq.y] = aux;
					aux = this.estado[der.x][der.y];
					this.estado[der.x][der.y] = this.estado[der.x+1][der.y];
					this.estado[der.x+1][der.y] = this.estado[der.x+2][der.y];
					this.estado[der.x+2][der.y] = aux;
				}
				break;
			
		default:
			break;
		}
		
		if(estado[posBlanco1.x][posBlanco1.y]!=TipoFicha.blanco1 && estado[posBlanco2.x][posBlanco2.y]!=TipoFicha.blanco2){
			System.out.println("pincho saliendo de mov H");
			System.exit(0);
		}
		//System.out.println("Blancos Despues:");
		//System.out.println(this.estado[posBlanco1.x][posBlanco1.y]);
		//System.out.println(this.estado[posBlanco2.x][posBlanco2.y]);
	}

	private Point movePos(Point pos, Movimientos tipoMov) {
		TipoFicha aux;
		Point posFin = new Point();
		
		System.out.println("Blanco antes:");
		System.out.println(this.estado[pos.x][pos.y]);
		
		int xPos = pos.x; /*FILA*/
		int yPos = pos.y; /*COLUMNA*/
//		System.out.println(this.estado[xPos][yPos]);
		switch (tipoMov) {
		case arriba:
			if (this.estado[xPos-1][yPos] == TipoFicha.cuadradop) {
				aux = this.estado[xPos][yPos];
				this.estado[xPos][yPos] = this.estado[xPos-1][yPos];
				this.estado[xPos-1][yPos] = aux;
				posFin.setLocation(xPos-1, yPos);
			}else {
				aux = this.estado[xPos][yPos];
				this.estado[xPos][yPos] = this.estado[xPos-1][yPos];
				this.estado[xPos-1][yPos] = TipoFicha.rectv;
				this.estado[xPos-2][yPos] = aux;
				posFin.setLocation(xPos-2, yPos);
			}
			break;
		case abajo: 
			if (this.estado[xPos+1][yPos] == TipoFicha.cuadradop) {
				aux = this.estado[xPos][yPos];
				this.estado[xPos][yPos] = this.estado[xPos+1][yPos];
				this.estado[xPos+1][yPos] = aux;
				posFin.setLocation(xPos+1, yPos);
			}else {
				aux = this.estado[xPos][yPos];
				this.estado[xPos][yPos] = this.estado[xPos+1][yPos];
				this.estado[xPos+1][yPos] = TipoFicha.nada;
				this.estado[xPos+2][yPos] = aux;
				posFin.setLocation(xPos+2, yPos);
			}
			break;
		case izquierda:
			if (this.estado[xPos][yPos-1] == TipoFicha.cuadradop) {
				aux = this.estado[xPos][yPos];
				this.estado[xPos][yPos] = this.estado[xPos][yPos-1];
				this.estado[xPos][yPos-1] = aux;
				posFin.setLocation(xPos, yPos-1);
			}else {
				aux = this.estado[xPos][yPos];
				this.estado[xPos][yPos] = TipoFicha.nada;
				this.estado[xPos][yPos-1] = TipoFicha.recth;
				this.estado[xPos][yPos-2] = aux;
				posFin.setLocation(xPos, yPos-2);
			}
			break;
		case derecha:
			if (this.estado[xPos][yPos+1] == TipoFicha.cuadradop) {
				aux = this.estado[xPos][yPos];
				this.estado[xPos][yPos] = this.estado[xPos][yPos+1];
				this.estado[xPos][yPos+1] = aux;
				posFin.setLocation(xPos, yPos+1);
			}else {
				aux = this.estado[xPos][yPos];
				this.estado[xPos][yPos] = TipoFicha.recth;
				this.estado[xPos][yPos+1] = TipoFicha.nada;
				this.estado[xPos][yPos+2] = aux;
				posFin.setLocation(xPos, yPos+2);
			}
			break;
			
		default:
			break;
		}
		
		System.out.println("Blanco despues:");
		System.out.println(this.estado[posFin.x][posFin.y]);
		
		return posFin;
	}
	
	@Override
	public String toString() {
		String resp = "Tablero:\n";
		
		for(TipoFicha[] a: this.estado){
			for(TipoFicha b: a) {
				resp+=b.name()+"\t";
			}
			resp+="\n";
		}
		
		return resp;
	}
	
//	public String espejadoStr() {
//		String resp = "Tablero:\n";
//		
//		for(TipoFicha[] a: this.espejado){
//			for(TipoFicha b: a) {
//				resp+=b.name()+"\t";
//			}
//			resp+="\n";
//		}
//		
//		return resp;
//	}
}
